#ifndef SERVER_HPP
#define SERVER_HPP

/* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <iostream>

class ServerBase
{
protected:

    static void error(const char *msg)
    {
        perror(msg);
        exit(1);
    }


    static int ServerStart(uint16_t portno)
    {
        //    socklen_t clilen;
        struct sockaddr_in serv_addr;


        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0)
            error("ERROR opening socket");
        bzero((char *) &serv_addr, sizeof(serv_addr));

        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = INADDR_ANY;
        serv_addr.sin_port = htons(portno);

        const int       optVal = 1;
        const socklen_t optLen = sizeof(optVal);

        if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void*) &optVal, optLen)!=0)
        {
            error("ERROR on setsockopt");
        }

        if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        {
            error("ERROR on binding");
        }

        listen(sockfd,5);
        //    clilen = sizeof(cli_addr);

        return sockfd;
    }


    static ssize_t ServerSend(int sockfd, const void* pData, size_t nData)
    {
        struct sockaddr_in cli_addr;
        socklen_t clilen;
        clilen = sizeof(cli_addr);

        int newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0)
            error("ERROR on accept");

        //        std::cout<<"send frame\n"<<std::flush;

        ssize_t n = write(newsockfd,pData,nData);

        if (n < 0) {
            error("ERROR writing to socket");
        }

        //        std::cout<<"end send frame "<<n<<" \n"<<std::flush;


        close(newsockfd);
        return n;
    }

    static ssize_t ServerReceive(int sockfd, void* pData, size_t nData)
    {
        struct sockaddr_in cli_addr;
        socklen_t clilen;
        clilen = sizeof(cli_addr);


        int newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0)
            error("ERROR on accept");

        //        std::cout<<"receive pos\n"<<std::flush;

        ssize_t n = read(newsockfd,pData,nData);
        if (n < 0) {
            error("ERROR reading from socket");
        }

        close(newsockfd);

        //        std::cout<<"end receive pos "<< n <<" \n"<<std::flush;

        return n;
    }



public:
    bool go;

    ServerBase()
    {

    }



    virtual void task(char buffer[256], std::string &response)
    {        
        if(strncmp(buffer,"END",3)==0)
        {
            std::cout<<"task";
            go=false;
            sleep(1);
            response="show qr code\n";
        }
    }



    void start(uint16_t portno=7654, size_t nBuffer=1024)
    {
        int sockfd=ServerStart(portno);
        char *buffer=new char[nBuffer];
        ssize_t n;
        go=true;

        while (go)
        {
            bzero(buffer,nBuffer);
            n=ServerReceive(sockfd, buffer, nBuffer-1);
            if (n < 0) break;
            printf("Here is the message: %s\n",buffer);
            std::cout.flush();
            std::string response="";
            task(buffer, response);
            std::cout<<"end task. response="<<response<<"\n"<<std::flush;
            n=ServerSend(sockfd, response.c_str(), response.size());
            if (n < 0) break;
        }
        close(sockfd);
        delete []buffer;
    }


    void start2(uint16_t portno=7654, size_t nBuffer=1024)
    {
        int sockfd=ServerStart(portno);
        char *buffer=new char[nBuffer];
        ssize_t n;
        go=true;

        while (go)
        {
            bzero(buffer,nBuffer);
            n=ServerReceive(sockfd, buffer, nBuffer-1);
            if (n < 0) break;
//            printf("Here is the message: %s\n",buffer);
            std::cout.flush();
            std::string response="";
            task(buffer, response);
//            std::cout<<"end task. response="<<response<<"\n"<<std::flush;
//            n=ServerSend(sockfd, response.c_str(), response.size());
//            if (n < 0) break;
        }
        close(sockfd);
        delete []buffer;
    }

};







#endif // SERVER_HPP

